// 类成员存储位置测试文件

class Dog {
  // 1. 实例属性（字段初始化器）- 在实例上
  name = '默认名字';
  defaultAge = 0;

  // 2. 静态属性 - 在构造函数自身上
  static species = '犬科';
  static count = 0;

  // 3. 构造函数中的属性 - 在实例上
  constructor(name, age) {
    this.name = name; // 覆盖字段初始化器的值
    this.color = 'black'; // 新增的实例属性
    this.age = age; // 触发 setter
    Dog.count++; // 更新静态属性
  }

  // 4. 普通方法 - 在原型上（Dog.prototype）
  bark() {
    console.log(`${this.name}: 汪汪汪！`);
  }

  // 5. getter/setter - 访问器在原型上，数据在实例上
  get age() {
    return this._age + '岁';
  }

  set age(value) {
    if (typeof value !== 'number') {
      throw new TypeError('年龄必须是数字');
    }
    this._age = value < 0 ? 0 : value;
  }

  // 6. 箭头函数方法（字段初始化器） - 在实例上
  run = () => {
    console.log(`${this.name} 正在跑步...`);
  };

  // 7. 静态方法 - 在构造函数自身上
  static create(name, age) {
    return new Dog(name, age);
  }

  static getCount() {
    return `当前共有 ${Dog.count} 只狗`;
  }
}

// 创建实例
const dog1 = new Dog('旺财', 3);
const dog2 = new Dog('小黑', 5);

console.log('==================== 测试开始 ====================\n');

// ========== 测试 1：实例上的属性 ==========
console.log('【测试 1】实例上的属性');
console.log('dog1.hasOwnProperty("name"):', dog1.hasOwnProperty('name')); // true
console.log('dog1.hasOwnProperty("color"):', dog1.hasOwnProperty('color')); // true
console.log('dog1.hasOwnProperty("_age"):', dog1.hasOwnProperty('_age')); // true
console.log('dog1.hasOwnProperty("defaultAge"):', dog1.hasOwnProperty('defaultAge')); // true
console.log('dog1.hasOwnProperty("run"):', dog1.hasOwnProperty('run')); // true（箭头函数）
console.log('');

// ========== 测试 2：原型上的成员 ==========
console.log('【测试 2】原型上的成员');
console.log('dog1.hasOwnProperty("bark"):', dog1.hasOwnProperty('bark')); // false
console.log('Dog.prototype.hasOwnProperty("bark"):', Dog.prototype.hasOwnProperty('bark')); // true
console.log('Dog.prototype.hasOwnProperty("age"):', Dog.prototype.hasOwnProperty('age')); // true（访问器）
console.log('');

// ========== 测试 3：构造函数上的成员 ==========
console.log('【测试 3】构造函数上的成员（静态成员）');
console.log('Dog.hasOwnProperty("species"):', Dog.hasOwnProperty('species')); // true
console.log('Dog.hasOwnProperty("count"):', Dog.hasOwnProperty('count')); // true
console.log('Dog.hasOwnProperty("create"):', Dog.hasOwnProperty('create')); // true
console.log('dog1.hasOwnProperty("species"):', dog1.hasOwnProperty('species')); // false
console.log('');

// ========== 测试 4：访问器属性的特性 ==========
console.log('【测试 4】getter/setter 属性描述符');
const ageDescriptor = Object.getOwnPropertyDescriptor(Dog.prototype, 'age');
console.log('age 属性描述符:', ageDescriptor);
console.log('是访问器属性:', 'get' in ageDescriptor && 'set' in ageDescriptor);
console.log('');

// ========== 测试 5：普通方法的特性 ==========
console.log('【测试 5】普通方法属性描述符');
const barkDescriptor = Object.getOwnPropertyDescriptor(Dog.prototype, 'bark');
console.log('bark 方法描述符:', barkDescriptor);
console.log('是数据属性:', 'value' in barkDescriptor);
console.log('');

// ========== 测试 6：箭头函数 vs 普通方法 ==========
console.log('【测试 6】箭头函数 vs 普通方法');
console.log('每个实例的 run 方法是否相同:', dog1.run === dog2.run); // false（每个实例一份）
console.log('每个实例的 bark 方法是否相同:', dog1.bark === dog2.bark); // true（共享原型）
console.log('');

// ========== 测试 7：this 指向 ==========
console.log('【测试 7】this 指向测试');
const bark1 = dog1.bark;
const run1 = dog1.run;

try {
  bark1(); // 普通方法，this 丢失，会报错（严格模式）或 undefined
} catch (e) {
  console.log('普通方法 this 丢失:', e.message);
}

run1(); // 箭头函数，this 永远指向 dog1，正常执行
console.log('');

// ========== 测试 8：静态成员访问 ==========
console.log('【测试 8】静态成员访问');
console.log('Dog.species:', Dog.species); // '犬科'
console.log('Dog.count:', Dog.count); // 2
console.log('Dog.getCount():', Dog.getCount()); // '当前共有 2 只狗'
console.log('dog1.species:', dog1.species); // undefined（实例不能直接访问）
console.log('dog1.constructor.species:', dog1.constructor.species); // '犬科'（通过构造函数访问）
console.log('');

// ========== 测试 9：使用静态方法创建实例 ==========
console.log('【测试 9】使用静态方法创建实例');
const dog3 = Dog.create('小白', 2);
console.log('dog3.name:', dog3.name);
console.log('dog3 instanceof Dog:', dog3 instanceof Dog);
console.log('Dog.count:', Dog.count); // 3
console.log('');

// ========== 测试 10：完整的原型链 ==========
console.log('【测试 10】原型链查看');
console.log('dog1 的所有自有属性:', Object.getOwnPropertyNames(dog1));
console.log('Dog.prototype 的所有属性:', Object.getOwnPropertyNames(Dog.prototype));
console.log('Dog 的所有属性:', Object.getOwnPropertyNames(Dog));
console.log('');

// ========== 测试 11：实际功能测试 ==========
console.log('【测试 11】实际功能测试');
dog1.bark(); // 旺财: 汪汪汪！
dog1.run(); // 旺财 正在跑步...
console.log('dog1.age:', dog1.age); // 3岁
dog1.age = 4;
console.log('dog1.age:', dog1.age); // 4岁
console.log('');

// ========== 总结 ==========
console.log('==================== 总结 ====================');
console.log(`
✅ 在实例上（每个对象独立）:
   - 字段初始化器定义的属性: name, defaultAge
   - 构造函数中定义的属性: color, _age
   - 箭头函数方法: run

✅ 在原型上（所有实例共享）:
   - 普通方法: bark
   - getter/setter 访问器: age

✅ 在构造函数上（类的静态成员）:
   - 静态属性: species, count
   - 静态方法: create, getCount

💡 关键区别：
   - 实例属性每个对象一份，占用内存多
   - 原型方法所有实例共享，节省内存
   - 静态成员属于类本身，不属于实例
   - 箭头函数的 this 永远指向定义时的实例
   - 普通方法的 this 指向调用者（可能变化）
`);

console.log('==================== 测试结束 ====================');

